"""
有声读物阅读进度跟踪服务
"""

import asyncio
import json
from typing import Dict, List, Any, Optional, Tuple
from dataclasses import dataclass, asdict
from datetime import datetime, timedelta
from enum import Enum
import math

from ...core.exceptions import BusinessLogicError, NotFoundError, ValidationError
from ...core.logging import get_logger, audit_logger
from ...core.config import settings
from ...models.audiobook import AudioBook, AudioBookChapter, AudioBookProgress
from ...repositories.audiobook import (
    AudioBookRepository,
    AudioBookChapterRepository,
    AudioBookProgressRepository
)
from ...services.cache import cache_service, CacheContext

logger = get_logger(__name__)


class ProgressEvent(Enum):
    """进度事件类型"""
    PLAY_START = "play_start"
    PLAY_PAUSE = "play_pause"
    PLAY_RESUME = "play_resume"
    PLAY_COMPLETE = "play_complete"
    SEEK = "seek"
    CHAPTER_CHANGE = "chapter_change"
    SPEED_CHANGE = "speed_change"
    BOOKMARK_ADD = "bookmark_add"
    BOOKMARK_REMOVE = "bookmark_remove"


@dataclass
class Bookmark:
    """书签"""
    id: str
    chapter_index: int
    position: float
    title: str
    description: str
    created_at: datetime
    audiobook_id: str
    user_id: str


@dataclass
class PlaybackSession:
    """播放会话"""
    id: str
    audiobook_id: str
    user_id: str
    start_time: datetime
    end_time: Optional[datetime] = None
    start_position: float = 0.0
    end_position: float = 0.0
    total_played_time: float = 0.0
    completed_chapters: List[int] = None
    
    def __post_init__(self):
        if self.completed_chapters is None:
            self.completed_chapters = []


@dataclass
class ProgressSnapshot:
    """进度快照"""
    current_chapter: int
    current_position: float
    total_progress: float
    playback_speed: float
    timestamp: datetime
    session_id: Optional[str] = None


class ProgressTracker:
    """进度跟踪器"""
    
    def __init__(self, session=None):
        self.session = session
        self._audiobook_repo = None
        self._chapter_repo = None
        self._progress_repo = None
        
        # 活跃会话
        self.active_sessions: Dict[str, Dict[str, PlaybackSession]] = {}  # user_id -> {audiobook_id: session}
    
    @property
    def audiobook_repo(self):
        if self._audiobook_repo is None:
            self._audiobook_repo = AudioBookRepository(self.session)
        return self._audiobook_repo
    
    @property
    def chapter_repo(self):
        if self._chapter_repo is None:
            self._chapter_repo = AudioBookChapterRepository(self.session)
        return self._chapter_repo
    
    @property
    def progress_repo(self):
        if self._progress_repo is None:
            self._progress_repo = AudioBookProgressRepository(self.session)
        return self._progress_repo
    
    async def start_playback(
        self,
        audiobook_id: str,
        user_id: str,
        chapter_index: Optional[int] = None,
        position: Optional[float] = None
    ) -> PlaybackSession:
        """开始播放"""
        try:
            # 验证有声读物存在
            audiobook = await self.audiobook_repo.get(audiobook_id)
            if not audiobook:
                raise NotFoundError(f"Audiobook {audiobook_id} not found")
            
            # 获取章节信息
            chapters = await self.chapter_repo.get_by_audiobook(audiobook_id)
            if not chapters:
                raise ValidationError("No chapters found")
            
            # 获取当前进度
            progress = await self.progress_repo.get_user_progress(user_id, audiobook_id)
            
            # 确定起始位置
            if chapter_index is None:
                chapter_index = progress.current_chapter if progress else 0
            
            if position is None:
                position = progress.current_position if progress else 0.0
            
            # 验证章节索引
            if chapter_index < 0 or chapter_index >= len(chapters):
                raise ValidationError(f"Invalid chapter index: {chapter_index}")
            
            # 创建播放会话
            session = PlaybackSession(
                id=f"{user_id}_{audiobook_id}_{int(datetime.utcnow().timestamp())}",
                audiobook_id=audiobook_id,
                user_id=user_id,
                start_time=datetime.utcnow(),
                start_position=position,
                completed_chapters=progress.chapter_progress.get("completed", []) if progress and progress.chapter_progress else []
            )
            
            # 记录活跃会话
            if user_id not in self.active_sessions:
                self.active_sessions[user_id] = {}
            self.active_sessions[user_id][audiobook_id] = session
            
            # 记录事件
            await self._record_event(
                user_id=user_id,
                audiobook_id=audiobook_id,
                event_type=ProgressEvent.PLAY_START,
                data={
                    "chapter_index": chapter_index,
                    "position": position,
                    "session_id": session.id
                }
            )
            
            logger.info(f"Started playback session {session.id}")
            return session
            
        except Exception as e:
            logger.error(f"Failed to start playback: {e}")
            raise
    
    async def update_progress(
        self,
        user_id: str,
        audiobook_id: str,
        chapter_index: int,
        position: float,
        total_duration: Optional[float] = None,
        auto_save: bool = True
    ) -> Optional[AudioBookProgress]:
        """更新进度"""
        try:
            # 获取当前会话
            session = self._get_active_session(user_id, audiobook_id)
            
            # 验证有声读物
            chapters = await self.chapter_repo.get_by_audiobook(audiobook_id)
            if not chapters:
                raise ValidationError("No chapters found")
            
            # 计算总进度
            total_progress = await self._calculate_total_progress(
                chapters, chapter_index, position, total_duration
            )
            
            # 获取或创建进度记录
            progress = await self.progress_repo.get_user_progress(user_id, audiobook_id)
            
            if progress:
                # 更新现有进度
                progress.current_chapter = chapter_index
                progress.current_position = position
                progress.total_progress = total_progress
                progress.last_read_at = datetime.utcnow()
                
                # 更新章节进度
                if not progress.chapter_progress:
                    progress.chapter_progress = {}
                
                # 检查章节是否完成
                chapter = chapters[chapter_index] if chapter_index < len(chapters) else None
                if chapter and position >= chapter.duration * 0.95:  # 95%完成
                    if "completed" not in progress.chapter_progress:
                        progress.chapter_progress["completed"] = []
                    if chapter_index not in progress.chapter_progress["completed"]:
                        progress.chapter_progress["completed"].append(chapter_index)
                
                # 更新会话
                if session:
                    session.end_position = position
                    if session.completed_chapters is None:
                        session.completed_chapters = []
                    
                    new_completed = set(session.completed_chapters)
                    new_completed.update(progress.chapter_progress.get("completed", []))
                    session.completed_chapters = list(new_completed)
                
            else:
                # 创建新进度记录
                progress_data = {
                    "user_id": user_id,
                    "audiobook_id": audiobook_id,
                    "current_chapter": chapter_index,
                    "current_position": position,
                    "total_progress": total_progress,
                    "last_read_at": datetime.utcnow(),
                    "chapter_progress": {
                        "completed": [chapter_index] if chapter and position >= chapter.duration * 0.95 else []
                    }
                }
                
                progress = await self.progress_repo.create_with_attributes(**progress_data)
            
            # 自动保存
            if auto_save:
                await self.progress_repo.update(progress, {})
                
                # 使缓存失效
                cache_key = f"user_progress:{user_id}:{audiobook_id}"
                await cache_service.delete(cache_key)
            
            logger.debug(f"Updated progress for user {user_id}, audiobook {audiobook_id}: {total_progress:.2%}")
            return progress
            
        except Exception as e:
            logger.error(f"Failed to update progress: {e}")
            raise
    
    async def pause_playback(self, user_id: str, audiobook_id: str) -> Optional[AudioBookProgress]:
        """暂停播放"""
        try:
            session = self._get_active_session(user_id, audiobook_id)
            if not session:
                return None
            
            # 计算播放时间
            session.end_time = datetime.utcnow()
            session.total_played_time = (session.end_time - session.start_time).total_seconds()
            
            # 获取当前进度并保存
            progress = await self.progress_repo.get_user_progress(user_id, audiobook_id)
            if progress:
                # 更新总阅读时间
                progress.total_read_time += session.total_played_time
                await self.progress_repo.update(progress, {})
                
                # 使缓存失效
                cache_key = f"user_progress:{user_id}:{audiobook_id}"
                await cache_service.delete(cache_key)
            
            # 记录事件
            await self._record_event(
                user_id=user_id,
                audiobook_id=audiobook_id,
                event_type=ProgressEvent.PLAY_PAUSE,
                data={
                    "session_id": session.id,
                    "played_time": session.total_played_time
                }
            )
            
            logger.info(f"Paused playback session {session.id}")
            return progress
            
        except Exception as e:
            logger.error(f"Failed to pause playback: {e}")
            raise
    
    async def resume_playback(
        self,
        user_id: str,
        audiobook_id: str
    ) -> Optional[PlaybackSession]:
        """恢复播放"""
        try:
            # 检查是否有活跃会话
            existing_session = self._get_active_session(user_id, audiobook_id)
            if existing_session:
                return existing_session
            
            # 获取当前进度
            progress = await self.progress_repo.get_user_progress(user_id, audiobook_id)
            
            # 开始新会话
            session = await self.start_playback(
                audiobook_id=audiobook_id,
                user_id=user_id,
                chapter_index=progress.current_chapter if progress else None,
                position=progress.current_position if progress else None
            )
            
            # 记录事件
            await self._record_event(
                user_id=user_id,
                audiobook_id=audiobook_id,
                event_type=ProgressEvent.PLAY_RESUME,
                data={
                    "session_id": session.id
                }
            )
            
            logger.info(f"Resumed playback for user {user_id}, audiobook {audiobook_id}")
            return session
            
        except Exception as e:
            logger.error(f"Failed to resume playback: {e}")
            raise
    
    async def complete_playback(self, user_id: str, audiobook_id: str) -> Optional[AudioBookProgress]:
        """完成播放"""
        try:
            session = self._get_active_session(user_id, audiobook_id)
            
            # 更新进度到100%
            progress = await self.progress_repo.get_user_progress(user_id, audiobook_id)
            if progress:
                progress.total_progress = 1.0
                progress.last_read_at = datetime.utcnow()
                
                # 获取所有章节
                chapters = await self.chapter_repo.get_by_audiobook(audiobook_id)
                if chapters:
                    progress.current_chapter = len(chapters) - 1
                    progress.current_position = chapters[-1].duration
                
                # 标记所有章节为完成
                progress.chapter_progress = {
                    "completed": list(range(len(chapters))) if chapters else []
                }
                
                # 更新会话
                if session:
                    session.end_time = datetime.utcnow()
                    session.total_played_time = (session.end_time - session.start_time).total_seconds()
                    progress.total_read_time += session.total_played_time
                
                await self.progress_repo.update(progress, {})
                
                # 使缓存失效
                cache_key = f"user_progress:{user_id}:{audiobook_id}"
                await cache_service.delete(cache_key)
                
                # 清除活跃会话
                self._clear_active_session(user_id, audiobook_id)
                
                # 记录事件
                await self._record_event(
                    user_id=user_id,
                    audiobook_id=audiobook_id,
                    event_type=ProgressEvent.PLAY_COMPLETE,
                    data={
                        "session_id": session.id if session else None,
                        "total_played_time": session.total_played_time if session else 0
                    }
                )
                
                logger.info(f"Completed playback for user {user_id}, audiobook {audiobook_id}")
                return progress
            
            return None
            
        except Exception as e:
            logger.error(f"Failed to complete playback: {e}")
            raise
    
    async def add_bookmark(
        self,
        user_id: str,
        audiobook_id: str,
        chapter_index: int,
        position: float,
        title: str,
        description: str = ""
    ) -> Bookmark:
        """添加书签"""
        try:
            # 获取进度记录
            progress = await self.progress_repo.get_user_progress(user_id, audiobook_id)
            if not progress:
                raise NotFoundError("No progress found for this audiobook")
            
            # 创建书签
            bookmark = Bookmark(
                id=f"{user_id}_{audiobook_id}_{int(datetime.utcnow().timestamp())}",
                chapter_index=chapter_index,
                position=position,
                title=title,
                description=description,
                created_at=datetime.utcnow(),
                audiobook_id=audiobook_id,
                user_id=user_id
            )
            
            # 添加到进度记录
            if not progress.bookmarks:
                progress.bookmarks = []
            
            progress.bookmarks.append(asdict(bookmark))
            await self.progress_repo.update(progress, {})
            
            # 记录事件
            await self._record_event(
                user_id=user_id,
                audiobook_id=audiobook_id,
                event_type=ProgressEvent.BOOKMARK_ADD,
                data={
                    "bookmark_id": bookmark.id,
                    "title": title
                }
            )
            
            logger.info(f"Added bookmark {bookmark.id}")
            return bookmark
            
        except Exception as e:
            logger.error(f"Failed to add bookmark: {e}")
            raise
    
    async def remove_bookmark(
        self,
        user_id: str,
        audiobook_id: str,
        bookmark_id: str
    ) -> bool:
        """删除书签"""
        try:
            # 获取进度记录
            progress = await self.progress_repo.get_user_progress(user_id, audiobook_id)
            if not progress or not progress.bookmarks:
                return False
            
            # 查找并删除书签
            original_count = len(progress.bookmarks)
            progress.bookmarks = [
                bm for bm in progress.bookmarks 
                if bm.get("id") != bookmark_id
            ]
            
            if len(progress.bookmarks) < original_count:
                await self.progress_repo.update(progress, {})
                
                # 使缓存失效
                cache_key = f"user_progress:{user_id}:{audiobook_id}"
                await cache_service.delete(cache_key)
                
                # 记录事件
                await self._record_event(
                    user_id=user_id,
                    audiobook_id=audiobook_id,
                    event_type=ProgressEvent.BOOKMARK_REMOVE,
                    data={
                        "bookmark_id": bookmark_id
                    }
                )
                
                logger.info(f"Removed bookmark {bookmark_id}")
                return True
            
            return False
            
        except Exception as e:
            logger.error(f"Failed to remove bookmark: {e}")
            raise
    
    async def get_progress(
        self,
        user_id: str,
        audiobook_id: str,
        include_bookmarks: bool = True
    ) -> Optional[AudioBookProgress]:
        """获取阅读进度"""
        try:
            progress = await self.progress_repo.get_user_progress(user_id, audiobook_id)
            
            if progress and not include_bookmarks:
                # 返回不包含书签的进度
                progress_copy = AudioBookProgress(
                    id=progress.id,
                    audiobook_id=progress.audiobook_id,
                    user_id=progress.user_id,
                    current_chapter=progress.current_chapter,
                    current_position=progress.current_position,
                    total_progress=progress.total_progress,
                    total_read_time=progress.total_read_time,
                    last_read_at=progress.last_read_at,
                    created_at=progress.created_at,
                    updated_at=progress.updated_at
                )
                return progress_copy
            
            return progress
            
        except Exception as e:
            logger.error(f"Failed to get progress: {e}")
            raise
    
    async def get_reading_history(
        self,
        user_id: str,
        limit: int = 50,
        include_progress: bool = False
    ) -> List[Dict[str, Any]]:
        """获取阅读历史"""
        try:
            history = await self.progress_repo.get_user_reading_history(user_id, limit)
            
            if not include_progress:
                # 只返回基本信息
                return [
                    {
                        "audiobook_id": prog.audiobook_id,
                        "total_progress": prog.total_progress,
                        "last_read_at": prog.last_read_at.isoformat() if prog.last_read_at else None
                    }
                    for prog in history
                ]
            
            # 包含详细进度信息
            result = []
            for prog in history:
                # 获取有声读物信息
                audiobook = await self.audiobook_repo.get(prog.audiobook_id)
                
                result.append({
                    "audiobook_id": prog.audiobook_id,
                    "audiobook_title": audiobook.title if audiobook else None,
                    "total_progress": prog.total_progress,
                    "current_chapter": prog.current_chapter,
                    "current_position": prog.current_position,
                    "total_read_time": prog.total_read_time,
                    "last_read_at": prog.last_read_at.isoformat() if prog.last_read_at else None,
                    "bookmarks_count": len(prog.bookmarks) if prog.bookmarks else 0
                })
            
            return result
            
        except Exception as e:
            logger.error(f"Failed to get reading history: {e}")
            raise
    
    async def get_progress_statistics(
        self,
        user_id: str,
        audiobook_id: Optional[str] = None
    ) -> Dict[str, Any]:
        """获取进度统计"""
        try:
            if audiobook_id:
                # 单个有声读物统计
                progress = await self.get_progress(user_id, audiobook_id)
                if not progress:
                    return {}
                
                chapters = await self.chapter_repo.get_by_audiobook(audiobook_id)
                
                return {
                    "audiobook_id": audiobook_id,
                    "total_progress": progress.total_progress,
                    "current_chapter": progress.current_chapter,
                    "current_position": progress.current_position,
                    "total_read_time": progress.total_read_time,
                    "completed_chapters": len(progress.chapter_progress.get("completed", [])) if progress.chapter_progress else 0,
                    "total_chapters": len(chapters),
                    "bookmarks_count": len(progress.bookmarks) if progress.bookmarks else 0,
                    "last_read_at": progress.last_read_at.isoformat() if progress.last_read_at else None,
                    "completion_rate": progress.total_progress,
                    "estimated_completion_time": self._estimate_completion_time(progress, chapters)
                }
            else:
                # 用户总体统计
                history = await self.progress_repo.get_user_reading_history(user_id, 1000)
                
                total_read_time = sum(prog.total_read_time for prog in history)
                completed_books = sum(1 for prog in history if prog.total_progress >= 0.95)
                in_progress_books = sum(1 for prog in history if 0 < prog.total_progress < 0.95)
                
                return {
                    "total_books": len(history),
                    "completed_books": completed_books,
                    "in_progress_books": in_progress_books,
                    "total_read_time": total_read_time,
                    "average_read_time": total_read_time / len(history) if history else 0,
                    "total_bookmarks": sum(len(prog.bookmarks) if prog.bookmarks else 0 for prog in history),
                    "last_read_at": max((prog.last_read_at for prog in history if prog.last_read_at), default=None)
                }
                
        except Exception as e:
            logger.error(f"Failed to get progress statistics: {e}")
            raise
    
    async def sync_progress_across_devices(
        self,
        user_id: str,
        audiobook_id: str,
        device_data: Dict[str, Any]
    ) -> Dict[str, Any]:
        """跨设备同步进度"""
        try:
            # 获取当前进度
            current_progress = await self.get_progress(user_id, audiobook_id)
            
            # 获取设备数据
            device_progress = device_data.get("progress", {})
            device_timestamp = datetime.fromisoformat(device_data.get("timestamp", datetime.utcnow().isoformat()))
            
            sync_result = {
                "updated": False,
                "conflict": False,
                "final_progress": None
            }
            
            if not current_progress:
                # 没有现有进度，使用设备数据
                await self.update_progress(
                    user_id=user_id,
                    audiobook_id=audiobook_id,
                    chapter_index=device_progress.get("current_chapter", 0),
                    position=device_progress.get("current_position", 0)
                )
                sync_result["updated"] = True
                
            else:
                # 比较时间戳，决定使用哪个进度
                if device_timestamp > current_progress.last_read_at:
                    # 设备进度更新，使用设备数据
                    await self.update_progress(
                        user_id=user_id,
                        audiobook_id=audiobook_id,
                        chapter_index=device_progress.get("current_chapter", current_progress.current_chapter),
                        position=device_progress.get("current_position", current_progress.current_position)
                    )
                    sync_result["updated"] = True
                elif abs((device_timestamp - current_progress.last_read_at).total_seconds()) < 60:
                    # 时间差很小，可能存在冲突
                    sync_result["conflict"] = True
                    
                    # 使用更进的进度
                    device_total = device_progress.get("total_progress", 0)
                    if device_total > current_progress.total_progress:
                        await self.update_progress(
                            user_id=user_id,
                            audiobook_id=audiobook_id,
                            chapter_index=device_progress.get("current_chapter", current_progress.current_chapter),
                            position=device_progress.get("current_position", current_progress.current_position)
                        )
                        sync_result["updated"] = True
            
            # 返回最终进度
            final_progress = await self.get_progress(user_id, audiobook_id)
            sync_result["final_progress"] = final_progress.to_dict() if final_progress else None
            
            logger.info(f"Synced progress for user {user_id}, audiobook {audiobook_id}")
            return sync_result
            
        except Exception as e:
            logger.error(f"Failed to sync progress: {e}")
            raise
    
    def _get_active_session(self, user_id: str, audiobook_id: str) -> Optional[PlaybackSession]:
        """获取活跃会话"""
        return self.active_sessions.get(user_id, {}).get(audiobook_id)
    
    def _clear_active_session(self, user_id: str, audiobook_id: str):
        """清除活跃会话"""
        if user_id in self.active_sessions:
            if audiobook_id in self.active_sessions[user_id]:
                del self.active_sessions[user_id][audiobook_id]
    
    async def _calculate_total_progress(
        self,
        chapters: List[AudioBookChapter],
        current_chapter: int,
        current_position: float,
        total_duration: Optional[float] = None
    ) -> float:
        """计算总进度"""
        if not chapters:
            return 0.0
        
        if total_duration:
            # 如果提供了总时长，直接计算
            total_chapter_duration = sum(ch.duration for ch in chapters)
            current_chapter_duration = sum(chapters[i].duration for i in range(current_chapter))
            
            if total_chapter_duration > 0:
                return (current_chapter_duration + current_position) / total_chapter_duration
        else:
            # 基于章节数量计算
            chapter_progress = current_position / chapters[current_chapter].duration if chapters[current_chapter].duration > 0 else 0
            return (current_chapter + chapter_progress) / len(chapters)
        
        return 0.0
    
    def _estimate_completion_time(
        self,
        progress: AudioBookProgress,
        chapters: List[AudioBookChapter]
    ) -> Optional[datetime]:
        """估算完成时间"""
        if not chapters or progress.total_progress >= 0.95:
            return None
        
        # 简单的线性估算
        if progress.total_read_time > 0:
            remaining_progress = 1.0 - progress.total_progress
            estimated_additional_time = (progress.total_read_time / progress.total_progress) * remaining_progress
            
            return datetime.utcnow() + timedelta(seconds=estimated_additional_time)
        
        return None
    
    async def _record_event(
        self,
        user_id: str,
        audiobook_id: str,
        event_type: ProgressEvent,
        data: Dict[str, Any]
    ):
        """记录事件"""
        try:
            # 这里可以记录到数据库或日志
            event_data = {
                "user_id": user_id,
                "audiobook_id": audiobook_id,
                "event_type": event_type.value,
                "data": data,
                "timestamp": datetime.utcnow().isoformat()
            }
            
            # 记录到审计日志
            audit_logger.log_user_action(
                user_id=user_id,
                action=f"progress_{event_type.value}",
                resource="audiobook_progress",
                details=event_data
            )
            
            # 可以在这里添加事件队列处理
            logger.debug(f"Recorded progress event: {event_type.value}")
            
        except Exception as e:
            logger.error(f"Failed to record event: {e}")


# 全局进度跟踪器实例
progress_tracker = ProgressTracker()
